package com.instpower.things.helper;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.instpower.things.config.Configs;
import com.instpower.things.entity.Battery;
import com.instpower.things.entity.Cabinet;
import com.instpower.things.service.InstService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

import static com.instpower.things.config.Configs.CABINET_TIMEOUT;

@Component
public class Handler0902Util {
    final static Logger logger = LoggerFactory.getLogger("CORE");

    public static Handler0902Util handler0902Util;

    @PostConstruct
    public void init() {
        handler0902Util = this;
    }

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private final static ObjectMapper mapper = new ObjectMapper();

    private final static Integer cpus = Runtime.getRuntime().availableProcessors();

    @Autowired
    private InstService instService;

    private static final ExecutorService executor =
            new ThreadPoolExecutor(cpus, cpus * 2, 0L,
                    TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(100), new ThreadPoolExecutor.AbortPolicy());

    public static void updateCabinetBatteryStatus(Cabinet cabinet, Map<String,String> batteryMap) {
        String cabinetSn = cabinet.getSn();
        int rentNum = cabinet.getRentNum();
        int returnNum = cabinet.getReturnNum();
        List<Battery> batteryList = cabinet.getBatteries();

        executor.submit( () -> {
            try {
                Cabinet cachedCabinet = handler0902Util.instService.getCachedCabinet(cabinetSn);
                if ((cachedCabinet == null) || !handler0902Util.instService.compareCachedCabinet(cachedCabinet, cabinet)) {
                    // return/rent数量变化时才更新inst_cabinet_info表
                    logger.info("0902 机柜sn  {} 数据库更新.", cabinetSn);
                    handler0902Util.instService.updateCabinetBeat(cabinetSn,true, rentNum, returnNum);
                }
                //判断是否需要把心跳数据记录在inst_cabinet_log中
                compareAndUpdateCabinetBeat(cachedCabinet,cabinetSn);


                //电池柜状态5分钟内失效
                handler0902Util.redisTemplate.opsForValue().set(cabinetSn, mapper.writeValueAsString(cabinet),
                        CABINET_TIMEOUT, TimeUnit.MINUTES);

                //机柜有电池，更新数据库和电池缓存
                if(batteryList.size() > 0) {
                    List<Battery> updatedBatteryList = new ArrayList<>();

                    // filter all those no updated batteries
                    for (Battery battery : batteryList) {
                        String batterySn = battery.getSn();
                        Battery cachedBattery = handler0902Util.instService.getCachedBattery(batterySn);
                        if ((cachedBattery == null) || !handler0902Util.instService.compareCachedBattery(cachedBattery, battery)) {
                            updatedBatteryList.add(battery);
                        }
                    }

                    if(updatedBatteryList.size() > 0) {
                        logger.info("0902 电池数据库更新 {}", cabinetSn);
                        handler0902Util.instService.updateBatteryStatus(cabinetSn, updatedBatteryList);
                    }
                    handler0902Util.redisTemplate.opsForHash().putAll(Configs.BATTERY_HASHKEY, batteryMap);
                }
            } catch (Exception dex) {
                logger.error("机柜 {} 更新数据库和redis操作失败!",cabinetSn);
                logger.error(dex.getLocalizedMessage(),dex);
            }
    });
    }

    /**
     * 判断是否需要把心跳数据记录在inst_cabinet_log中
     * @param cachedCabinet
     * @param cabinetSn
     */
    private static void compareAndUpdateCabinetBeat(Cabinet cachedCabinet,String cabinetSn) {
        // 1.null == cachedCabinet :redis机柜信息有过期时间,如果redis查不到该机柜信息,不能保证机柜心跳是否保存到inst_cabinet_log,
        // 举例 3-1号 22:00 关机, 3-2号10:00 开机,这时候redis数据已经查不到了,但是需要把数据落库inst_cabinet_log
        //2.缓存最后的更新时间小于等于当天零时,也需要同步数据到inst_cabinet_log
        if(null == cachedCabinet || !DateUtil.parseDateTime(cachedCabinet.getLastUpdateTime()).after(DateUtil.getCurrentDayZeroTime())){
            handler0902Util.instService.updateCabinetLog(cabinetSn);
        }
    }


}
